#include "player.hpp"
#include <iostream>
#include <algorithm>

using namespace Shiny;
using namespace Shiny::Applejack;

Player::Player(World& w, Body& b, Applejack::View* camera) : IA(w, b), _camera(camera)
{
  _life             = 100;
  _damageResistance = 50;
  _damageResistanceModifier = 0;
  _speed            = 60;
  _speedModifier    = 0;
  _strength         = 10;
  _strengthModifier = 0;
  _movingLeft  = false;
  _movingUp    = false;
  _movingRight = false;
  _isPunching  = false;
  _isKicking   = false;
  _stunned     = false;

  _punchingLeft  = false;
  _punchingRight = false;

  _lookingLeft = false;

  // Can Jump
  Fixture* feets = b.Fixture("feets");

  if (feets)
  {
    feets->ContactBegin.Connect(*this, &Player::FootCollision);
    feets->ContactEnd.Connect(*this, &Player::FootEndCollision);
  }
  _canJump = false;
  _performingAction = false;

  _attackPunchRight   = new Attack(_punchingRight, b.Fixture("fist"));
  _attackPunchLeft    = new Attack(_punchingLeft,  b.Fixture("fistLeft"));
  _attackAirKickRight = new Attack(_kickingRight, b.Fixture("airFootRight"));
  _attackAirKickLeft  = new Attack(_kickingLeft,  b.Fixture("airFootLeft"));
  _attackAirKickLeft->effects.push_back(Attack::Effect(Attack::Effect::STUN, 100.f, 0.f));
  _attackAirKickRight->effects.push_back(Attack::Effect(Attack::Effect::STUN, 100.f, 0.f));
  _attacks.push_back(_attackPunchRight);
  _attacks.push_back(_attackPunchLeft);
  _attacks.push_back(_attackAirKickRight);
  _attacks.push_back(_attackAirKickLeft);
}

void Player::Run(void)
{
  b2Body* body  = _body.PhysicBody();
  float   speed = _speed + ((_speed / 100) * _speedModifier);
  bool    interruptChannelling = false;

  ApplyEffects();
  if (!_performingAction && !_stunned)
  {
      interruptChannelling = true;
      if (_movingLeft)
      {
	if (!_lookingLeft) _lookingLeft = true;
	if (_canJump)
	  body->ApplyLinearImpulse(b2Vec2(-speed, 0.f), body->GetWorldCenter());
	else
	  body->ApplyForceToCenter(b2Vec2(-speed * 5, 0.f));
      }
      else if (_movingRight)
      {
	if (_lookingLeft) _lookingLeft = false;
	if (_canJump)
	  body->ApplyLinearImpulse(b2Vec2(speed, 0.f),  body->GetWorldCenter());
	else
	  body->ApplyForceToCenter(b2Vec2(speed * 5, 0.f));
      }
      else if (_movingUp && _canJump && _jumpTimeout.GetElapsedTime().AsMilliseconds() > 500.f)
      {
	_jumpTimeout.Restart();
	body->ApplyLinearImpulse(b2Vec2(0.f, -(body->GetMass() * 7)), body->GetWorldCenter());
      }
      else
	  interruptChannelling = false;
  }

  if (_stunned && _stunTime < _stunTimeout.GetElapsedTime().AsMilliseconds())
  {
    _stunned = false;
    SetDefaultAnimation();
  }

  if (_isPunching && _punchTimeout.GetElapsedTime().AsMilliseconds() > 250)
  {
      _performingAction = false;
      _isPunching    = false;
      _punchingLeft  = false;
      _punchingRight = false;
      SetDefaultAnimation();
  }

  if ((_isKicking) && _kickTimeout.GetElapsedTime().AsMilliseconds() > 250)
  {
      SetDefaultAnimation();
      _isKicking    = false;
      _kickingLeft  = false;
      _kickingRight = false;
      _performingAction = false;
  }


  for_each(_attacks.begin(), _attacks.end(), Attack::Functor);

  if (interruptChannelling)
      this->InterruptChannelling();
}

void Player::SetDefaultAnimation()
{
    if (_stunned)
	_body.Sprite().SetAnimation(5);
    if (!_canJump)
	_body.Sprite().SetAnimation(2);
    else
	_body.Sprite().SetAnimation(0);
}

void Player::SetControls(Controls& controls)
{
    Control *spell1, *spell2, *spell3, *spell4, *punch, *kick;

    _controls     = &controls;
    _controlLeft  = controls["Move Left"];
    _controlUp    = controls["Jump"];
    _controlRight = controls["Move Right"];
    _controlPunch = controls["Punch"];

    if (_controlLeft)
    {
	_controlLeft->Pressed.Connect(*this,   &Player::Moving);
	_controlLeft->Released.Connect(*this,  &Player::StopMoving);
    }
    if (_controlRight)
    {
	_controlRight->Pressed.Connect(*this,  &Player::Moving);
	_controlRight->Released.Connect(*this, &Player::StopMoving);
    }
    if (_controlUp)
    {
	_controlUp->Pressed.Connect(*this,        &Player::Moving);
	_controlUp->Released.Connect(*this,       &Player::StopMoving);
    }

    spell1 = controls["Spell 1"];
    spell2 = controls["Spell 2"];
    spell3 = controls["Spell 3"];
    spell4 = controls["Spell 4"];
    punch  = controls["Punch"];
    kick   = controls["Kick"];
    if (spell1)    spell1->Released.Connect(*this,    &Player::SpellFirst);
    if (spell2)    spell2->Released.Connect(*this,    &Player::SpellSecond);
    if (spell3)    spell3->Released.Connect(*this,    &Player::SpellThird);
    if (spell4)    spell4->Released.Connect(*this,    &Player::SpellUltimate);
    if (punch)     punch->Pressed.Connect(*this,        &Player::Punch);
    if (kick)      kick->Pressed.Connect(*this,        &Player::Kick);

    EventManager::Get()->Controls().Add(controls);
}

void Player::AddSpellDisplay(Shiny::Widgets::ProgressBar*& spell, int it, const sf::Texture* texture)
{
    if (!texture)
    {
      ErrorManager::Get()->AddError("Missing texture for a spell", ErrorManager::Warning);
      return ;
    }
    float paddingRight  = 0.f;

    paddingRight  = (43.f * it);
    spell = new Shiny::Widgets::ProgressBar();
    if (texture)
    {
	SpriteAnimate::AnimationArray animationArray;

	animationArray.push_back(SpriteAnimate::Animation(texture));
	spell->BackgroundSprite().SetAnimations(animationArray);
	spell->BackgroundSprite().SetAnimation(0);
	spell->SetSize(spell->BackgroundSprite().GetSize());
    }
    //spell->SetAlign(Shiny::Widgets::Right, Shiny::Widgets::Bot);
    spell->SetColor(sf::Color(255.f, 255.f, 100.f, 130.f));
    //spell->SetPadding(0.f, 0.f, paddingRight, -20.f);

    sf::Vector2f position = _camera->GetPosition();
    sf::Vector2f size(_camera->GetWidth(), _camera->GetHeight());

    position.x += size.x - (43.f * 4);
    position.y += size.y - 43.f;
    position.x += paddingRight;
    spell->SetPosition(position);
    _world.Hud()->AddWidget(spell);
}

void Player::Moving(sf::Event event)
{
  if (_stunned)
    return ;
  if      ((*_controlUp) == event)
    _movingUp    = true;
  else if ((*_controlLeft) == event)
  {
    _body.Sprite().FlipX(true);
    _movingLeft  = true;
  }
  else if ((*_controlRight) == event)
  {
    _body.Sprite().FlipX(false);
    _movingRight = true;
  }
}

void Player::StopMoving(sf::Event event)
{
    if      ((*_controlUp) == event)
      _movingUp    = false;
    else if ((*_controlLeft) == event)
      _movingLeft  = false;
    else if ((*_controlRight) == event)
      _movingRight = false;
}

void Player::FootCollision(b2Contact*)
{
  _body.Sprite().SetAnimation(0);
  _canJump++;
}

void Player::FootEndCollision(b2Contact*)
{
  _canJump--;
  if (!_canJump)
    _body.Sprite().SetAnimation(2);
}

void Player::Punch(sf::Event)
{
  if (_isPunching || _stunned || _performingAction)
    return ;
  this->InterruptChannelling();
  _performingAction = true;
  _body.Sprite().SetAnimation(1);
  _animationPunchObserver = _body.Sprite().AnimationFinished.Connect(*this, &Player::EndPunch);
}

void Player::EndPunch(unsigned int)
{
  _body.Sprite().AnimationFinished.Disconnect(_animationPunchObserver);
  (_lookingLeft ? _punchingLeft : _punchingRight) = true;
  _isPunching    = true;
  _punchTimeout.Restart();
}

void Player::Kick(sf::Event)
{
    if (_stunned || _performingAction)
      return ;
    if (!_canJump)
    {
	_body.Sprite().SetAnimation(4);
	_animationKickObserver = _body.Sprite().AnimationFinished.Connect(*this, &Player::EndKick);
	_performingAction = true;
    }
}

void Player::EndKick(unsigned int)
{
    _body.Sprite().AnimationFinished.Disconnect(_animationKickObserver);
    (_lookingLeft ? _kickingLeft : _kickingRight) = true;
    _isKicking = true;
    _kickTimeout.Restart();
}

void Player::GetKicked(int damage, const Attack::Effects& effects)
{
  Attack::Effects::const_iterator it          = effects.begin();
  Attack::Effects::const_iterator end         = effects.end();
  float                           damageTaken = damage - (damage / (_damageResistance + _damageResistanceModifier));

  _life -= damageTaken;
  InterruptChannelling();
  LifeUpdated.Emit(_life);
  for (; it != end ; ++it) AddBuff(*it);
}

void Player::Stun(float time)
{
    if (!_stunned || (_stunTime - _stunTimeout.GetElapsedTime().AsMilliseconds()) < time)
    {
	_stunTime = time;
	_stunTimeout.Restart();
	_stunned = true;
	AnimationInterupted();
	_body.Sprite().SetAnimation(5);
    }
}

void Player::AnimationInterupted()
{
    _performingAction = false;
    _isPunching       = false;
    _isKicking        = false;
    _body.Sprite().AnimationFinished.Disconnect(this->_animationKickObserver);
    _body.Sprite().AnimationFinished.Disconnect(this->_animationPunchObserver);
}
